#include <stdio.h>
#include <stdlib.h>
#include "CAStar_Tests.h"
#include "../src/CAStar.h"
#include "../src/CAStar_private.h"

int CAStar_Tests_initReset() {
	int failure = 0;

	CAStar_init(10,5);

	if(area.width != 10) {
		failure = 1;
	}
	if(area.height != 5) {
		failure = 1;
	}
	nodes.nodes[9*5+4]->g=2;
	nodes.nodes[9*5+4]->h=5;
	Node* n1 = nodes.nodes[9*5+4];
	if(n1->g!=2) {
		printf("n1.g should be 2 is %f\n",n1->g);
		failure = 1;
	}
	if(n1->h!=5) {
		printf("n1.h should be 5 is %f\n",n1->h);
		failure = 1;
	}

	CAStar_resetPath(1);
	n1 = nodes.nodes[9*5+4];
	if(n1->g!=-123) {
		printf("n1.g should be -123 is %f\n",n1->g);
		failure = 1;
	}
	if(n1->h!=-123) {
		printf("n1.h should be -123 is %f\n",n1->h);
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_initFree() {
	int failure = 0;

	CAStar_init(10,5);
	CAStar_free();
	CAStar_init(8,4);
	CAStar_free();

	CAStar_free(); //already freed
	return failure;
}

int CAStar_Tests_listAddRemove() {
	int failure = 0;

	CAStar_init(20,14);

	nodes.nodes[0*area.height+0]->g = 11;
	nodes.nodes[0*area.height+0]->h = 12;
	nodes.nodes[1*area.height+1]->g = 21;
	nodes.nodes[1*area.height+1]->h = 22;

	if(openList.size!=0) {
		printf("OpenList size should be 0, is %d\n",openList.size);
		failure = 1;
	}

	CAStar_private_List_add(&openList, nodes.nodes[0*area.height+0]);
	CAStar_private_List_add(&openList, nodes.nodes[1*area.height+1]);

	if(openList.size!=2) {
		printf("OpenList size should be 2, is %d\n",openList.size);
		failure = 1;
	}

	CAStar_private_List_remove(&openList, 0, 0);
	if(openList.size!=1) {
		printf("OpenList size should be 1, is %d\n",openList.size);
		failure = 1;
	}
	CAStar_private_List_remove(&openList, 0, 0);
	if(openList.size!=1) {
		printf("OpenList size should be 1, is %d\n",openList.size);
		failure = 1;
	}
	CAStar_private_List_remove(&openList, 1, 1);
	if(openList.size!=0) {
		printf("OpenList size should be 0, is %d\n",openList.size);
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_listGet() {
	int failure = 0;

	CAStar_init(10,7);

	Node* n1 = nodes.nodes[1*area.height+1]; // node(1,1)
	n1->g = 10;
	n1->h = 11;
	Node* n2 = nodes.nodes[2*area.height+3]; // node(2,3)
	n2->g = 20;
	n2->h = 21;
	Node* n3 = nodes.nodes[4*area.height+5]; // node(4,5)
	n3->g = 30;
	n3->h = 31;

	CAStar_private_List_add(&openList, n3);
	CAStar_private_List_add(&openList, n1);
	CAStar_private_List_add(&openList, n2);

	Node* n = CAStar_private_List_getLowestFNode();
	if(CAStar_private_getF(n) != 21) {
		printf("F should be 21, is %f\n",CAStar_private_getF(n));
		failure = 1;
	}

	CAStar_private_List_remove(&openList, 1, 1);
	n = CAStar_private_List_getLowestFNode();
	if(CAStar_private_getF(n) != 41) {
		printf("F should be 41, is %f\n",CAStar_private_getF(n));
		failure = 1;
	}

	CAStar_private_List_remove(&openList, 2, 3);
	n = CAStar_private_List_getLowestFNode();
	if(CAStar_private_getF(n) != 61) {
		printf("F should be 61, is %f\n",CAStar_private_getF(n));
		failure = 1;
	}

	CAStar_private_List_remove(&openList, 2, 3);
	n = CAStar_private_List_getLowestFNode();
	if(CAStar_private_getF(n) != 61) {
		printf("F should be 61, is %f\n",CAStar_private_getF(n));
		failure = 1;
	}

	CAStar_private_List_remove(&openList, 4, 5);
	n = CAStar_private_List_getLowestFNode();
	if(CAStar_private_getF(n) != -1) {
		printf("F should be -1, is %f\n",CAStar_private_getF(n));
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_getDistance() {
	int failure = 0;

	CAStar_init(10,7);

	Node* n1 = nodes.nodes[2*area.height+4]; // 2,4
	Node* n2 = nodes.nodes[6*area.height+4]; // 6,4

	double dist = CAStar_private_getDistance(n1, n2);
	if(dist != 4*UNIT_SIZE) {
		printf("Distance should be %d, is %f\n",4*UNIT_SIZE,dist);
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_getNeighbors() {
	int failure = 0;

	CAStar_init(10,7);

	Node* n1 = nodes.nodes[0*area.height+0]; // 0,0
	CAStar_private_calculateNeighbors(n1);
	if(neighbors.size != 3) {
		printf("neighbors.size should be 3, is %d\n",neighbors.size);
		failure = 1;
	}

	Node* n2 = nodes.nodes[1*area.height+1]; // 1,1
	CAStar_private_calculateNeighbors(n2);
	if(neighbors.size != 8) {
		printf("neighbors.size should be 8, is %d\n",neighbors.size);
		failure = 1;
	}
	CAStar_editArea(0,1,TERRAIN_IMPASSABLE);
	CAStar_private_calculateNeighbors(n2);
	if(neighbors.size != 7) {
		printf("neighbors.size should be 7, is %d\n",neighbors.size);
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_calculatePath1() {
	int failure = 0;

	CAStar_init(10,7);

	CAStar_calculatePath(1,1,1,1,0);
	if(lastCalculatedPath.nodesCount != 1) {
		printf("lastCalculatedPath.nodesCount should be 1, is %d\n", lastCalculatedPath.nodesCount);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[0]->x != 1 || lastCalculatedPath.nodes[0]->y != 1) {
		printf("lastCalculatedPath.nodes[0] (x,y) should be (1,1), is (%d, %d)\n", lastCalculatedPath.nodes[0]->x, lastCalculatedPath.nodes[0]->y);
		failure = 1;
	}

	CAStar_calculatePath(2,2,2,2,0);
	if(lastCalculatedPath.nodesCount != 1) {
		printf("lastCalculatedPath.nodesCount should be 1, is %d\n", lastCalculatedPath.nodesCount);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[0]->x != 2 || lastCalculatedPath.nodes[0]->y != 2) {
		printf("lastCalculatedPath.nodes[0] (x,y) should be (2,2), is (%d, %d)\n", lastCalculatedPath.nodes[0]->x, lastCalculatedPath.nodes[0]->y);
		failure = 1;
	}

	CAStar_free();
	return failure;
}

int CAStar_Tests_calculatePath2() {
	int failure = 0;

	CAStar_init(10,7);

	CAStar_calculatePath(1,1,4,1,0);
	if(lastCalculatedPath.nodesCount != 4) {
		printf("lastCalculatedPath.nodesCount should be 3, is %d\n", lastCalculatedPath.nodesCount);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[0]->x != 1 || lastCalculatedPath.nodes[0]->y != 1) {
		printf("lastCalculatedPath.nodes[0] should be (1,1), is (%d,%d)\n", lastCalculatedPath.nodes[0]->x ,lastCalculatedPath.nodes[0]->y);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[1]->x != 2 || lastCalculatedPath.nodes[1]->y != 1) {
		printf("lastCalculatedPath.nodes[1] should be (2,1), is (%d,%d)\n", lastCalculatedPath.nodes[1]->x ,lastCalculatedPath.nodes[1]->y);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[2]->x != 3 || lastCalculatedPath.nodes[2]->y != 1) {
		printf("lastCalculatedPath.nodes[2] should be (3,1), is (%d,%d)\n", lastCalculatedPath.nodes[2]->x ,lastCalculatedPath.nodes[2]->y);
		failure = 1;
	}

	if(lastCalculatedPath.nodes[3]->x != 4 || lastCalculatedPath.nodes[3]->y != 1) {
		printf("lastCalculatedPath.nodes[3] should be (4,1), is (%d,%d)\n", lastCalculatedPath.nodes[3]->x ,lastCalculatedPath.nodes[3]->y);
		failure = 1;
	}

	CAStar_printPath();

	CAStar_free();
	return failure;
}

int CAStar_Tests_calculatePath3() {
	int failure = 0;

	CAStar_init(10,7);
	CAStar_editArea(3,0, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,1, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,2, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,3, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,4, TERRAIN_IMPASSABLE);

	CAStar_calculatePath(1,1,4,1,0);

	CAStar_printPath();

	CAStar_free();
	return failure;
}

int CAStar_Tests_calculatePath4() {
	int failure = 0;

	CAStar_init(7,20);
	CAStar_editArea(3,0, TERRAIN_IMPASSABLE);

	CAStar_editArea(3,2, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,3, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,4, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,5, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,6, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,7, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,8, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,9, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,10, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,11, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,12, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,13, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,14, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,15, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,16, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,17, TERRAIN_IMPASSABLE);

	CAStar_calculatePath(1,2,5,2,0);
	CAStar_printPath();

	printf("+++++++++++++++++++++++\n");

	CAStar_editArea(3,1,TERRAIN_SOME_WHAT_PASSABLE);
	CAStar_resetPath(0);
	CAStar_calculatePath(1,2,5,2,30);
	CAStar_printPath();

	printf("+++++++++++++++++++++++\n");

	CAStar_editArea(3,18, TERRAIN_IMPASSABLE);
	CAStar_editArea(3,19, TERRAIN_IMPASSABLE);
	CAStar_resetPath(0);
	CAStar_calculatePath(1,2,5,2,30);
	CAStar_printPath();

	CAStar_free();
	return failure;
}

int main(void) {
	//========================================
	//========================================
	//========================================
	int failure = 0;
	//========================================
	//========================================
	//========================================

	failure = CAStar_Tests_initReset();
	if(failure==0) {
		printf("CAStar_Tests_initReset [OK]\n");
	} else {
		printf("CAStar_Tests_initReset [Failure]\n");
	}

	failure = CAStar_Tests_initFree();
	if(failure==0) {
		printf("CAStar_Tests_initFree [OK]\n");
	} else {
		printf("CAStar_Tests_initFree [Failure]\n");
	}

	failure = CAStar_Tests_listAddRemove();
	if(failure==0) {
		printf("CAStar_Tests_listAddRemove [OK]\n");
	} else {
		printf("CAStar_Tests_listAddRemove [Failure]\n");
	}

	failure = CAStar_Tests_listGet();
	if(failure==0) {
		printf("CAStar_Tests_listGet [OK]\n");
	} else {
		printf("CAStar_Tests_listGet [Failure]\n");
	}

	failure = CAStar_Tests_getDistance();
	if(failure==0) {
		printf("CAStar_Tests_getDistance [OK]\n");
	} else {
		printf("CAStar_Tests_getDistance [Failure]\n");
	}

	failure = CAStar_Tests_getNeighbors();
	if(failure==0) {
		printf("CAStar_Tests_getNeighbors [OK]\n");
	} else {
		printf("CAStar_Tests_getNeighbors [Failure]\n");
	}

	failure = CAStar_Tests_calculatePath1();
	if(failure==0) {
		printf("CAStar_Tests_calculatePath1 [OK]\n");
	} else {
		printf("CAStar_Tests_calculatePath1 [Failure]\n");
	}

	failure = CAStar_Tests_calculatePath2();
	if(failure==0) {
		printf("CAStar_Tests_calculatePath2 [OK]\n");
	} else {
		printf("CAStar_Tests_calculatePath2 [Failure]\n");
	}

	failure = CAStar_Tests_calculatePath3();
	if(failure==0) {
		printf("CAStar_Tests_calculatePath3 [OK]\n");
	} else {
		printf("CAStar_Tests_calculatePath3 [Failure]\n");
	}

	failure = CAStar_Tests_calculatePath4();
	if(failure==0) {
		printf("CAStar_Tests_calculatePath4 [OK]\n");
	} else {
		printf("CAStar_Tests_calculatePath4 [Failure]\n");
	}

	//========================================
	//========================================
	//========================================
	printf("+++ Test suite executed +++\n");
	printf("+++ Failure: [%d]\n",failure);
	//========================================
	//========================================
	//========================================

	return failure;
}
